﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Diagnostics;

namespace SL8.SL.Controls
{
    /// <summary>
    /// 任务日历
    /// </summary>

    [TemplatePart(Name = TaskCalendar.PART_Root, Type = typeof(Grid))]
    [TemplatePart(Name = TaskCalendar.PART_MonthView, Type = typeof(Grid))]
    [TemplatePart(Name = TaskCalendar.PART_DayItemsView, Type = typeof(Grid))]
    [TemplatePart(Name = TaskCalendar.PART_WeekDayView, Type = typeof(Grid))]
    [TemplatePart(Name = TaskCalendar.PART_DayView, Type = typeof(Grid))]
    [TemplatePart(Name = TaskCalendar.PART_NextButton, Type = typeof(Button))]
    [TemplatePart(Name = TaskCalendar.PART_PrevButton, Type = typeof(Button))]
    [TemplatePart(Name = TaskCalendar.PART_DayView_HeaderButton, Type = typeof(Button))]
    [TemplatePart(Name = TaskCalendar.PART_MonthView_HeaderButton, Type = typeof(Button))]
    public class TaskCalendar : Control, IControl
    {
        private const string PART_Root = "Root";
        private const string PART_MonthView = "MonthView";
        private const string PART_DayItemsView = "DayItemsView";
        private const string PART_DayView = "DayView";
        private const string PART_WeekDayView = "WeekDayView";
        private const string PART_PrevButton = "PrevButton";
        private const string PART_NextButton = "NextButton";
        private const string PART_DayView_HeaderButton = "DayView_HeaderButton";
        private const string PART_MonthView_HeaderButton = "MonthView_HeaderButton";

        private Grid Root { get; set; }
        private Grid MonthView { get; set; }
        private Grid DayView { get; set; }
        private Grid DayItemsView { get; set; }
        private Grid WeekDayView { get; set; }
        private Button NextButton { get; set; }
        private Button PrevButton { get; set; }
        private Button DayView_HeaderButton { get; set; }
        private Button MonthView_HeaderButton { get; set; }

        private DateTime CurrentDate
        {
            get
            {
                return DateTime.Now;
            }
        }

        private System.Collections.ObjectModel.ObservableCollection<TaskCalendarDayItemInfo> _dayItemInfos;
        private System.Collections.ObjectModel.ObservableCollection<TaskCalendarDayItemInfo> DayItemInfos
        {
            get
            {
                if (this._dayItemInfos == null)
                {
                    this._dayItemInfos = new System.Collections.ObjectModel.ObservableCollection<TaskCalendarDayItemInfo>();
                }

                return this._dayItemInfos;
            }
        }

        private System.Collections.ObjectModel.ObservableCollection<TaskCalendarMonthItemInfo> _monthItemInfos;
        private System.Collections.ObjectModel.ObservableCollection<TaskCalendarMonthItemInfo> MonthItemInfos
        {
            get
            {
                if (this._monthItemInfos == null)
                {
                    this._monthItemInfos = new System.Collections.ObjectModel.ObservableCollection<TaskCalendarMonthItemInfo>();
                }

                return this._monthItemInfos;
            }
        }

        #region 起始日期
        public static readonly DependencyProperty VisualStartDateProperty = DependencyProperty.Register("VisualStartDate",
            typeof(DateTime),
            typeof(TaskCalendar),
            new PropertyMetadata(DateTime.Now));

        public DateTime VisualStartDate
        {
            get
            {
                return (DateTime)base.GetValue(TaskCalendar.VisualStartDateProperty);
            }

            private set
            {
                base.SetValue(TaskCalendar.VisualStartDateProperty, value);
            }
        }
        #endregion

        #region 视图
        public static readonly DependencyProperty TaskCalendarModeProperty = DependencyProperty.Register("TaskCalendarMode", typeof(TaskCalendarMode), typeof(TaskCalendar), new PropertyMetadata(TaskCalendarMode.Day, new PropertyChangedCallback(TaskCalendar.OnTaskCalendarModePropertyChanged)));

        /// <summary>
        /// 视图
        /// </summary>
        public TaskCalendarMode TaskCalendarMode
        {
            get
            {
                return (TaskCalendarMode)base.GetValue(TaskCalendar.TaskCalendarModeProperty);
            }

            set
            {
                base.SetValue(TaskCalendar.TaskCalendarModeProperty, value);
            }
        }

        private static void OnTaskCalendarModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TaskCalendar taskCalendar = d as TaskCalendar;
            Debug.Assert(taskCalendar != null, "c should not be null!");

            if (taskCalendar != null)
            {
                taskCalendar.UpdateTaskCalendarVisual();
            }
        }
        #endregion

        #region 工作日
        public static readonly DependencyProperty TaskDayOfWeekProperty = DependencyProperty.Register("TaskDayOfWeek",
            typeof(TaskDayOfWeek),
            typeof(TaskCalendar),
            new PropertyMetadata(TaskDayOfWeek.Monday | TaskDayOfWeek.Tuesday | TaskDayOfWeek.Wednesday | TaskDayOfWeek.Thursday | TaskDayOfWeek.Friday, new PropertyChangedCallback(TaskCalendar.OnTaskDayOfWeekPropertyChanged)));

        /// <summary>
        /// 视图
        /// </summary>
        public TaskDayOfWeek TaskDayOfWeek
        {
            get
            {
                return (TaskDayOfWeek)base.GetValue(TaskCalendar.TaskDayOfWeekProperty);
            }

            set
            {
                base.SetValue(TaskCalendar.TaskDayOfWeekProperty, value);
            }
        }
        private static void OnTaskDayOfWeekPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TaskCalendar c = d as TaskCalendar;
            Debug.Assert(c != null, "c should not be null!");

            if (c != null)
            {
                c.TaskDayOfWeekPropertyChanged();
            }
        }

        private void TaskDayOfWeekPropertyChanged()
        {
            this.UpdateTaskDay();
        }
        #endregion

        #region 选中日期
        public static readonly DependencyProperty SelectedDateProperty =
            DependencyProperty.Register(
            "SelectedDate",
            typeof(DateTime?),
            typeof(TaskCalendar),
            new PropertyMetadata(OnSelectedDateChanged));

        [TypeConverter(typeof(DateTimeTypeConverter))]
        public DateTime? SelectedDate
        {
            get { return (DateTime?)GetValue(SelectedDateProperty); }
            set { SetValue(SelectedDateProperty, value); }
        }

        private static void OnSelectedDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TaskCalendar c = d as TaskCalendar;
            Debug.Assert(c != null, "c should not be null!");
            if (c != null)
            {
                c.UpdateSelectedDate();
            }
        }
        #endregion

        #region 日数据模版
        public static readonly DependencyProperty DayItemTemplateProperty = DependencyProperty.RegisterAttached("DayItemTemplate",
            typeof(DataTemplate),
            typeof(TaskCalendar), null);

        public DataTemplate DayItemTemplate
        {
            get
            {
                return (DataTemplate)base.GetValue(TaskCalendar.DayItemTemplateProperty);
            }

            set
            {
                base.SetValue(TaskCalendar.DayItemTemplateProperty, value);
            }
        }
        #endregion

        #region 星期数据模版
        public static readonly DependencyProperty WeekDayTemplateProperty = DependencyProperty.RegisterAttached("WeekDayTemplate",
            typeof(DataTemplate),
            typeof(TaskCalendar), null);

        public DataTemplate WeekDayTemplate
        {
            get
            {
                return (DataTemplate)base.GetValue(TaskCalendar.WeekDayTemplateProperty);
            }

            set
            {
                base.SetValue(TaskCalendar.WeekDayTemplateProperty, value);
            }
        }
        #endregion

        #region 月数据模版
        public static readonly DependencyProperty MonthTemplateProperty = DependencyProperty.RegisterAttached("MonthTemplate",
            typeof(DataTemplate),
            typeof(TaskCalendar), null);

        public DataTemplate MonthTemplate
        {
            get
            {
                return (DataTemplate)base.GetValue(TaskCalendar.MonthTemplateProperty);
            }

            set
            {
                base.SetValue(TaskCalendar.MonthTemplateProperty, value);
            }
        }
        #endregion

        public TaskCalendar()
        {
            this.DefaultStyleKey = typeof(TaskCalendar);
        }

        private void TaskCalendarModePropertyChanged()
        {
            if (this.TaskCalendarMode == Controls.TaskCalendarMode.Day)
            {
                if (this.DayItemsView != null)
                {
                    this.DayItemsView.Visibility = System.Windows.Visibility.Visible;
                }

                if (this.MonthView != null)
                {
                    this.MonthView.Visibility = System.Windows.Visibility.Collapsed;
                }
            }
            else if (this.TaskCalendarMode == Controls.TaskCalendarMode.Month)
            {
                if (this.DayItemsView != null)
                {
                    this.DayItemsView.Visibility = System.Windows.Visibility.Collapsed;
                }

                if (this.MonthView != null)
                {
                    this.MonthView.Visibility = System.Windows.Visibility.Visible;
                }
            }

            this.UpdateTaskCalendarVisual();
        }

        private void UpdateTaskCalendarVisual()
        {
            if (this.TaskCalendarMode == Controls.TaskCalendarMode.Day)
            {
                if (this.DayView != null)
                {
                    this.DayView.Visibility = System.Windows.Visibility.Visible;
                }

                if (this.MonthView != null)
                {
                    this.MonthView.Visibility = System.Windows.Visibility.Collapsed;
                }

                if (this.DayView_HeaderButton != null)
                {
                    this.DayView_HeaderButton.Visibility = System.Windows.Visibility.Visible;
                }

                if (this.MonthView_HeaderButton != null)
                {
                    this.MonthView_HeaderButton.Visibility = System.Windows.Visibility.Collapsed;
                }

                this.UpdateDayVisual();
                this.UpdateTaskDay();
            }
            else
            {
                if (this.DayView != null)
                {
                    this.DayView.Visibility = System.Windows.Visibility.Collapsed;
                }

                if (this.MonthView != null)
                {
                    this.MonthView.Visibility = System.Windows.Visibility.Visible;
                }

                if (this.DayView_HeaderButton != null)
                {
                    this.DayView_HeaderButton.Visibility = System.Windows.Visibility.Collapsed;
                }

                if (this.MonthView_HeaderButton != null)
                {
                    this.MonthView_HeaderButton.Visibility = System.Windows.Visibility.Visible;
                }

                this.UpdateMonthVisual();
            }
        }

        private void UpdateMonthVisual()
        {
            if (this.MonthTemplate == null || this.MonthView == null)
            {
                return;
            }

            for (int i = 0; i < 12; i++)
            {
                this.MonthItemInfos[i].Date = new DateTime(this.VisualStartDate.Year, i + 1, 1);

                this.MonthItemInfos[i].IsPass = this.MonthItemInfos[i].Date <= new DateTime(this.CurrentDate.Year, this.CurrentDate.Month, 1);

                this.MonthItemInfos[i].IsCurrentMonth = this.MonthItemInfos[i].Date.Year == this.CurrentDate.Year
                    && this.MonthItemInfos[i].Date.Month == this.CurrentDate.Month;
            }
        }

        private void UpdateDayVisual()
        {
            // 更新时间   
            this.VisualStartDate = new DateTime(this.VisualStartDate.Year, this.VisualStartDate.Month, 1);
            int i = 0;
            if (this.VisualStartDate == DateTime.MinValue)
            {
                i = 1;
            }

            for (; i < 42; i++)
            {
                this.DayItemInfos[i].Date = this.VisualStartDate.AddDays(i - (int)(this.VisualStartDate.DayOfWeek));
                this.DayItemInfos[i].IsPass = this.DayItemInfos[i].Date <= new DateTime(this.CurrentDate.Year, this.CurrentDate.Month, this.CurrentDate.Day);
                this.DayItemInfos[i].IsCurrentMonth = this.DayItemInfos[i].Date.Month == this.VisualStartDate.Month;
                this.DayItemInfos[i].IsCurrentDay = this.DayItemInfos[i].Date.Year == this.CurrentDate.Year
                    && this.DayItemInfos[i].Date.Month == this.CurrentDate.Month
                    && this.DayItemInfos[i].Date.Day == this.CurrentDate.Day;
            }

            this.UpdateSelectedDate();
        }

        #region 初始化
        private void InitTaskCalendarDayItems()
        {
            if (this.DayItemTemplate == null || this.DayItemsView == null)
            {
                return;
            }

            this.DayItemInfos.Clear();
            this.DayItemsView.Children.Clear();

            for (int i = 0; i < 42; i++)
            {
                this.DayItemInfos.Add(new TaskCalendarDayItemInfo());
                this.DayItemInfos[i].Selected += new EventHandler(TaskCalendar_Day_Selected);
            }

            for (int row = 0; row < 6; row++)
            {
                for (int col = 0; col < 7; col++)
                {
                    ContentControl content = new ContentControl();
                    content.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                    content.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch;
                    content.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                    content.VerticalContentAlignment = System.Windows.VerticalAlignment.Stretch;
                    content.Content = this.DayItemInfos[row * 7 + col];
                    content.ContentTemplate = this.DayItemTemplate;
                    Grid.SetRow(content, row);
                    Grid.SetColumn(content, col);
                    this.DayItemsView.Children.Add(content);
                }
            }
        }

        private void InitTaskCalendarMonthItems()
        {
            if (this.MonthTemplate == null || this.MonthView == null)
            {
                return;
            }

            this.MonthItemInfos.Clear();
            this.MonthView.Children.Clear();

            for (int i = 0; i < 12; i++)
            {
                this.MonthItemInfos.Add(new TaskCalendarMonthItemInfo());
                this.MonthItemInfos[i].Selected += new EventHandler(TaskCalendar_Month_Selected);
            }

            for (int row = 0; row < 3; row++)
            {
                for (int col = 0; col < 4; col++)
                {
                    ContentControl content = new ContentControl();
                    content.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                    content.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch;
                    content.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                    content.VerticalContentAlignment = System.Windows.VerticalAlignment.Stretch;
                    content.Content = this.MonthItemInfos[row * 4 + col];
                    content.ContentTemplate = this.MonthTemplate;
                    Grid.SetRow(content, row);
                    Grid.SetColumn(content, col);
                    this.MonthView.Children.Add(content);
                }
            }
        }

        private void InitWeekDays()
        {
            if (this.WeekDayTemplate == null || this.WeekDayView == null)
            {
                return;
            }

            string[] weekDayItems = new string[] { "日", "一", "二", "三", "四", "五", "六" };

            for (int i = 0; i < 7; i++)
            {
                ContentControl content = new ContentControl();
                content.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                content.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch;
                content.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                content.VerticalContentAlignment = System.Windows.VerticalAlignment.Stretch;
                content.Content = weekDayItems[i];
                content.ContentTemplate = this.WeekDayTemplate;
                Grid.SetColumn(content, i);
                this.WeekDayView.Children.Add(content);
            }
        }
        #endregion

        private void TaskCalendar_Day_Selected(object sender, EventArgs e)
        {
            this.SelectedDate = ((TaskCalendarDayItemInfo)sender).Date;
            this.UpdateSelectedDate();
        }

        private void TaskCalendar_Month_Selected(object sender, EventArgs e)
        {
            TaskCalendarMonthItemInfo info = (TaskCalendarMonthItemInfo)sender;
            this.VisualStartDate = new DateTime(info.Date.Year, info.Date.Month, 1);
            this.TaskCalendarMode = Controls.TaskCalendarMode.Day;
        }

        private void UpdateSelectedDate()
        {
            if (this.SelectedDate == null)
            {
                foreach (var item in DayItemInfos)
                {
                    item.IsChecked = false;
                }
            }
            else
            {
                foreach (var item in DayItemInfos)
                {
                    item.IsChecked = this.SelectedDate.Value.Year == item.Date.Year
                        && this.SelectedDate.Value.Month == item.Date.Month
                        && this.SelectedDate.Value.Day == item.Date.Day;
                }
            }
        }


        /// <summary>
        /// 更新工作日
        /// </summary>
        private void UpdateTaskDay()
        {
            foreach (var item in this.DayItemInfos)
            {
                switch (item.Date.DayOfWeek)
                {
                    case DayOfWeek.Friday:
                        item.IsWorkDay = ((TaskDayOfWeek.Friday & this.TaskDayOfWeek) == TaskDayOfWeek.Friday);
                        break;
                    case DayOfWeek.Monday:
                        item.IsWorkDay = ((TaskDayOfWeek.Monday & this.TaskDayOfWeek) == TaskDayOfWeek.Monday);
                        break;
                    case DayOfWeek.Saturday:
                        item.IsWorkDay = ((TaskDayOfWeek.Saturday & this.TaskDayOfWeek) == TaskDayOfWeek.Saturday);
                        break;
                    case DayOfWeek.Sunday:
                        item.IsWorkDay = ((TaskDayOfWeek.Sunday & this.TaskDayOfWeek) == TaskDayOfWeek.Sunday);
                        break;
                    case DayOfWeek.Thursday:
                        item.IsWorkDay = ((TaskDayOfWeek.Thursday & this.TaskDayOfWeek) == TaskDayOfWeek.Thursday);
                        break;
                    case DayOfWeek.Tuesday:
                        item.IsWorkDay = ((TaskDayOfWeek.Tuesday & this.TaskDayOfWeek) == TaskDayOfWeek.Tuesday);
                        break;
                    case DayOfWeek.Wednesday:
                        item.IsWorkDay = ((TaskDayOfWeek.Wednesday & this.TaskDayOfWeek) == TaskDayOfWeek.Wednesday);
                        break;
                    default:
                        break;
                }
            }
        }

        public override void OnApplyTemplate()
        {
            this.UnsubscribeFromTemplatePartEvents();
            base.OnApplyTemplate();

            this.MonthView = this.GetTemplateChild(TaskCalendar.PART_MonthView) as Grid;
            this.DayView = this.GetTemplateChild(TaskCalendar.PART_DayView) as Grid;
            this.DayItemsView = this.GetTemplateChild(TaskCalendar.PART_DayItemsView) as Grid;
            this.WeekDayView = this.GetTemplateChild(TaskCalendar.PART_WeekDayView) as Grid;
            this.Root = this.GetTemplateChild(TaskCalendar.PART_MonthView) as Grid;

            this.NextButton = this.GetTemplateChild(TaskCalendar.PART_NextButton) as Button;
            this.PrevButton = this.GetTemplateChild(TaskCalendar.PART_PrevButton) as Button;

            this.DayView_HeaderButton = this.GetTemplateChild(TaskCalendar.PART_DayView_HeaderButton) as Button;
            this.MonthView_HeaderButton = this.GetTemplateChild(TaskCalendar.PART_MonthView_HeaderButton) as Button;

            this.SubscribeToTemplatePartEvents();

            this.InitTaskCalendarDayItems();
            this.InitWeekDays();
            this.InitTaskCalendarMonthItems();

            this.UpdateTaskCalendarVisual();
        }

        #region 事件
        public void SubscribeToTemplatePartEvents()
        {
            if (this.NextButton != null)
            {
                this.NextButton.Click += new RoutedEventHandler(NextButton_Click);
            }

            if (this.PrevButton != null)
            {
                this.PrevButton.Click += new RoutedEventHandler(PrevButton_Click);
            }

            if (this.DayView_HeaderButton != null)
            {
                this.DayView_HeaderButton.Click += new RoutedEventHandler(DayView_HeaderButton_Click);
            }

            if (this.MonthView_HeaderButton != null)
            {
                this.MonthView_HeaderButton.Click += new RoutedEventHandler(MonthView_HeaderButton_Click);
            }
        }

        public void UnsubscribeFromTemplatePartEvents()
        {
            if (this.NextButton != null)
            {
                this.NextButton.Click -= new RoutedEventHandler(NextButton_Click);
            }

            if (this.PrevButton != null)
            {
                this.PrevButton.Click -= new RoutedEventHandler(PrevButton_Click);
            }
        }

        void MonthView_HeaderButton_Click(object sender, RoutedEventArgs e)
        {
            this.TaskCalendarMode = Controls.TaskCalendarMode.Day;
        }

        void DayView_HeaderButton_Click(object sender, RoutedEventArgs e)
        {
            this.TaskCalendarMode = Controls.TaskCalendarMode.Month;
        }

        void PrevButton_Click(object sender, RoutedEventArgs e)
        {
            if (this.TaskCalendarMode == Controls.TaskCalendarMode.Day)
            {
                this.VisualStartDate = this.VisualStartDate.AddMonths(-1);
                this.UpdateDayVisual();
            }
            else if (this.TaskCalendarMode == Controls.TaskCalendarMode.Month)
            {
                this.VisualStartDate = this.VisualStartDate.AddYears(-1);
                this.UpdateMonthVisual();
            }
        }

        void NextButton_Click(object sender, RoutedEventArgs e)
        {
            if (this.TaskCalendarMode == Controls.TaskCalendarMode.Day)
            {
                this.VisualStartDate = this.VisualStartDate.AddMonths(1);
                this.UpdateDayVisual();
            }
            else if (this.TaskCalendarMode == Controls.TaskCalendarMode.Month)
            {
                this.VisualStartDate = this.VisualStartDate.AddYears(1);
                this.UpdateMonthVisual();
            }
        }
        #endregion
    }
}
